﻿namespace GoFun.Tests

open NUnit.Framework
open FsCheck

open GoFun.Lib
open GoFun.Lib.Positionen

module private ChainGenerator =

    type private ChainBuildStatus = { Hull : (Position*(Position list)) Set; Items : Position Set }

    let private freeInSurrounding dim pos status =
        let isFree = status.Items |> switch Set.contains >> not
        surrounding dim pos
        |> List.filter isFree

    let private chooseFromHull status =
        Gen.elements status.Hull

    let private initBuilder dim =
        gen {
            let! pos = PosGenerator.choosePos dim
            let ps = Set.singleton pos
            let hl = Set.singleton (pos, surrounding dim pos)
            return { Hull = hl; Items = ps } 
        }

    let generateChain (dim : int) (maxCount : int) : Gen<Kette> =
        let addToHull status =
            gen {
                if Set.isEmpty status.Hull then return None else
                let! hullPos, posiblePs = chooseFromHull status
                let! nextPos = Gen.elements posiblePs
                let items' = Set.add nextPos status.Items
                match freeInSurrounding dim nextPos status with
                | [] -> return { status with Items = items' } |> Some
                | freePs ->
                    let h' = nextPos, freePs
                    let hull' = Set.add h' status.Hull
                    return { Hull = hull'; Items = items' } |> Some
            }
        let rec generate status n =
            gen {
                if n <= 0 then return status else
                let! tryAddHull = addToHull status
                match tryAddHull with
                | None -> return status
                | Some h -> return! generate h (n-1)
            }
        gen {
            let! init = initBuilder dim
            let! generated = generate init maxCount
            return generated.Items
        }